Option Explicit

'################################################################
' Descriptor
' 基础类型数据的数组引用
'################################################################

Implements IPrimitiveArrayRef

'################################################################
' Field
'################################################################
' 原始数组引用
Private delegatedArrRef As IPrimitiveArrayRef

' ------------------------------
' 在当前切片内部通过索引的计算来管理引用的数据
' ------------------------------
' 切片的起始索引
Private sliceStartIndex As Long

' 切片的步长
Private sliceStep As Long

' 当前切片的最大长度
' 运行时不变
Private sliceMaxIndex As Long

'################################################################
' Constructor
'################################################################
Private Sub Class_Initialize()
    sliceStartIndex = -1
    sliceStep = -1
    sliceMaxIndex = -1
End Sub

Private Sub Class_Terminate()
End Sub

'################################################################
' Init
'################################################################
' @param origin 原始 IPrimitiveArrayRef 对象
' @optional-param start 起始索引
' @optional-param stop 终止结束(最终不会包含 stop 的索引)
' @optional-param step 步长，默认 = 1，但是不能是 0
Public Sub Init(ByRef origin As IPrimitiveArrayRef, Optional ByRef startIdx As Variant, Optional ByRef stopIdx As Variant, Optional ByRef step As Variant=1)
    ' 1. 计算切片信息
    Dim sliceInfo As Variant
    sliceInfo = UtilsArray.computeArraySliceInfo(origin.Count, startIdx, stopIdx, step)

    ' 2. 保存被代理的数据对象
    Set delegatedArrRef = origin

    ' 3. 如果切片的计算结果中没有数据，则立刻终止
    If sliceInfo(3) <= 0 Then
        Exit Sub
    End If

    ' 4. 保存各个属性
    sliceStartIndex = sliceInfo(0)
    sliceStep = sliceInfo(2)
    sliceMaxIndex = sliceInfo(3) - 1
End Sub

'################################################################
' Property
'################################################################
' 获取当前数组引用中已使用的长度
Public Property Get IPrimitiveArrayRef_Count() As Long
    IPrimitiveArrayRef_Count = sliceMaxIndex + 1
End Property

' 获取当前数组引用的最大索引
Public Property Get IPrimitiveArrayRef_MaxIndex() As Long
    IPrimitiveArrayRef_MaxIndex = sliceMaxIndex
End Property

'################################################################
' Function
'################################################################
' 添加 count 个空数据到数组中
' 如果容量不足，将会自动扩容
' 空数据将会同一被设置成 Empty
'
' @param count 需要添加的空数据数量
Public Sub IPrimitiveArrayRef_AddNull(ByRef count As Long)
    Err.Raise 10000, Description:="PrimitiveArrayRefSlice.IPrimitiveArrayRef_AddNull: method is not support"
End Sub

' 获取指定索引下的数据
' @param index 数组索引，如果越界将会抛出异常
Public Function IPrimitiveArrayRef_Item(ByRef index As Long) As Variant
    ' 1. 检查参数
    If index < 0 Then
        Err.Raise 10010, Description:="PrimitiveArrayRefSlice.IPrimitiveArrayRef_Item: parameter[index]=" & index & " out of bounds. min index=0, max index[" & sliceMaxIndex & "]"
        Exit Function
    End If
    If index > sliceMaxIndex Then
        Err.Raise 10010, Description:="PrimitiveArrayRefSlice.IPrimitiveArrayRef_Item: parameter[index]=" & index & " out of bounds. min index=0, max index[" & sliceMaxIndex & "]"
        Exit Function
    End If

    ' 2. 返回数据
    '    计算真实索引 = 切片起始索引 + 目标索引 * 步长
    IPrimitiveArrayRef_Item = delegatedArrRef.unsafeItem(sliceStartIndex + index * sliceStep)
End Function

' 获取指定索引下的数据
' 获取数据时，将不会检查索引的范围，如果索引越界会产生异常
' 在可以保证索引一定安全的情况下，可以通过该方法来获取数据，通过省略索引范围检查来提升效率
' @param index 数组索引，如果越界将会抛出异常
Public Function IPrimitiveArrayRef_unsafeItem(ByRef index As Long) As Variant
    IPrimitiveArrayRef_Item = delegatedArrRef.unsafeItem(sliceStartIndex + index * sliceStep)
End Function

' 设置指定索引下的数据
' @param index 数组索引，如果越界将会抛出异常
' @param val 需要保存到数组中的数据
Public Sub IPrimitiveArrayRef_setVal(ByRef index As Long, ByRef val As Variant)
    ' 1. 检查参数
    If index < 0 Then
        Err.Raise 10010, Description:="PrimitiveArrayRefSlice.IPrimitiveArrayRef_setVal: parameter[index]=" & index & " out of bounds. min index=0, max index[" & sliceMaxIndex & "]"
        Exit Sub
    End If
    If index > sliceMaxIndex Then
        Err.Raise 10010, Description:="PrimitiveArrayRefSlice.IPrimitiveArrayRef_setVal: parameter[index]=" & index & " out of bounds. min index=0, max index[" & sliceMaxIndex & "]"
        Exit Sub
    End If

    If IsObject(val) Then
        Err.Raise 10004, Description:="PrimitiveArrayRefSlice.IPrimitiveArrayRef_setVal: parameter val must be not Object or Nothing"
        Exit Sub
    End If

    ' 2. 设置数据
    delegatedArrRef.setVal sliceStartIndex + index * sliceStep, val
End Sub

' 向数组末尾添加数据，如果数组容量不足将会自动扩容
' @param val 需要保存到数组中的数据
Public Sub IPrimitiveArrayRef_Add(ByRef val As Variant)
    Err.Raise 10000, Description:="PrimitiveArrayRefSlice.IPrimitiveArrayRef_Add: method is not support"
End Sub

' 从数组的开头添加数据，如果数组容量不足将会自动扩容
' @param val 需要保存到数组中的数据
Public Sub IPrimitiveArrayRef_AddFirst(ByRef val As Variant)
    Err.Raise 10000, Description:="PrimitiveArrayRefSlice.IPrimitiveArrayRef_AddFirst: method is not support"
End Sub

' 将数据 `val` 插入索引 `index` 的位置
' 支持的索引范围为 `0--IPrimitiveArrayRef.Count()`
'
' @param index 数据需要插入的索引
' @param val 需要保存到数组中的**对象类型数据**
Public Sub IPrimitiveArrayRef_Insert(ByRef index As Long, ByRef val As Variant)
    Err.Raise 10000, Description:="PrimitiveArrayRefSlice.IPrimitiveArrayRef_Insert: method is not support"
End Sub

' 清空数组内容
'
' @optional-param initSize 当前参数在切片下无效
Public Sub IPrimitiveArrayRef_clear(Optional ByRef initSize As Long = -1)
    Dim i As Long
    For i = 0 To sliceMaxIndex
        delegatedArrRef.setVal sliceStartIndex + i * sliceStep, Empty
    Next
End Sub

' 删除指定索引下的数据
'
' 如果索引超出了数组的边界，不会抛出异常，默认不会执行任何操作
' 该操作会使数组的可用长度 - 1
'
' @param index 数组索引
Public Sub IPrimitiveArrayRef_remove(ByRef index As Long)
    Err.Raise 10000, Description:="PrimitiveArrayRefSlice.IPrimitiveArrayRef_remove: method is not support"
End Sub

' 尝试从当前数组中搜索与参数 val 相同的数据，并删除
'
' 如果 needAll=False, 则只会删除第一个与 val 相同的数据
' 如果 needAll=True, 则会遍历整个数组并删除与 val 相同的所有数据
' 该操作会使数组的可用长度变小
'
' @param val, 需要删除的数据
' @optinal-param needAll 是否需要删除所有相同的数据
'                默认=False，只删除第一个和 val 相同的数据
Public Sub IPrimitiveArrayRef_removeVal(ByRef val As Variant, Optional ByRef needAll As Boolean = False)
    Err.Raise 10000, Description:="PrimitiveArrayRefSlice.IPrimitiveArrayRef_removeVal: method is not support"
End Sub

' 删除最后一个元素
Public Sub IPrimitiveArrayRef_removeLast()
    Err.Raise 10000, Description:="PrimitiveArrayRefSlice.IPrimitiveArrayRef_removeLast: method is not support"
End Sub

' 删除最后一个元素，并返回删除的数据
'
' @return 被删除的最后一个元素
'         如果当前数组引用中已经没有元素了，将会返回一个 `Empty`
Public Function IPrimitiveArrayRef_pollLast() As Variant
    Err.Raise 10000, Description:="PrimitiveArrayRefSlice.IPrimitiveArrayRef_pollLast: method is not support"
End Function

' 获取数组引用中的最后一个元素
' 如果当前数组引用是空的，则返回 `Empty`
Public Function IPrimitiveArrayRef_getLast() As Variant
    If sliceMaxIndex = -1 Then
        IPrimitiveArrayRef_getLast = Empty
    Else
        IPrimitiveArrayRef_getLast = delegatedArrRef.unsafeItem(sliceStartIndex + sliceMaxIndex * sliceStep)
    End If
End Function

' 合并另一个基础类型数组引用中的数据
' @param others, 另一个基础类型数组引用
Public Sub IPrimitiveArrayRef_mergeArrRef(ByRef others As IPrimitiveArrayRef)
    Err.Raise 10000, Description:="PrimitiveArrayRefSlice.IPrimitiveArrayRef_mergeArrRef: method is not support"
End Sub

' 生成当前数组引用的切片
' @optional-param startIdx 起始位置
' @optional-param stopIdx 终止位置(最终不会包含 stop 的索引)
' @optional-param step 步长
'                      默认值 = 1
'                      不能是 0，如果设置了 0 将会抛出异常
Public Function IPrimitiveArrayRef_slice(Optional ByRef startIdx As Variant, Optional ByRef stopIdx As Variant, Optional ByRef step As Variant = 1) As IPrimitiveArrayRef
    ' 生成切片
    Dim result As PrimitiveArrayRefSlice
    Set result = New PrimitiveArrayRefSlice
    result.Init Me, startIdx, stopIdx, Step

    ' return
    Set IPrimitiveArrayRef_slice = result
End Function

' 克隆当前数组引用对象，并生成一个 `PrimitiveArrayRef` 类型的真实数组引用
Public Function IPrimitiveArrayRef_clone() As IPrimitiveArrayRef
    ' 1. 创建一个和切片元素数量相同长度的数组
    Dim result As PrimitiveArrayRef
    Set result = UtilsArrayRef.createRealPrimitiveArrayRef(sliceMaxIndex + 1)

    ' 2. 遍历，从原始数组引用中获取数据，并保存到 result
    Dim i As Long
    For i = 0 To sliceMaxIndex
        result.Add delegatedArrRef.unsafeItem(sliceStartIndex + i * sliceStep)
    Next

    ' return
    Set IPrimitiveArrayRef_clone = result
End Function

' 复制索引 `[startIdx, endIdx)` 范围的内数据到新的数组引用对象中
' @optional-param startIdx 起始索引
' @optional-param endIdx 结束索引（不包括）
Public Function IPrimitiveArrayRef_copyToNewArray(Optional ByRef startIdx As Long = 0, Optional ByRef endIdx As Long = -1) As IPrimitiveArrayRef
    Dim result As PrimitiveArrayRef

    ' 1. 如果没有元素，则返回一个空对象
    If sliceMaxIndex = -1 Or startIdx > sliceMaxIndex  Then
        Set result = New PrimitiveArrayRef
        result.Init 0
        Set IPrimitiveArrayRef_copyToNewArray = result
        Exit Function
    End If

    ' 2. 计算真实的索引
    Dim realStartIdx As Long
    ' 这里表示最后一个元素的索引位置
    Dim realLastIdx As Long
    If startIdx < 0 Then
        realStartIdx = 0
    Else
        realStartIdx = startIdx
    End If

    If endIdx < 0 Then
        realLastIdx = sliceMaxIndex
    ElseIf endIdx > sliceMaxIndex Then
        realLastIdx = sliceMaxIndex
    Else 
        realLastIdx = endIdx - 1
    End If

    ' 快速失败
    ' 如果起始索引 > 最后一个元素的索引，则返回一个空结果
    If realStartIdx > realLastIdx Then
        Set result = New PrimitiveArrayRef
        result.Init 0
        Set IPrimitiveArrayRef_copyToNewArray = result
        Exit Function
    End If

    ' 3. 拷贝数据
    Set result = New PrimitiveArrayRef
    result.Init (realLastIdx - realStartIdx + 1)
    Set IPrimitiveArrayRef_copyToNewArray = result

    Dim i As Long
    For i = realStartIdx To realLastIdx
        IPrimitiveArrayRef_copyToNewArray.Add delegatedArrRef.unsafeItem(sliceStartIndex + i * sliceStep)
    Next

End Function

' 获取当前数组引用中，数组缓存的真实长度
Public Function IPrimitiveArrayRef_unsafeRealSize() As Long
    IPrimitiveArrayRef_unsafeRealSize = sliceMaxIndex + 1
End Function

' ==================================================
' 粘贴数据到 sheet
' ==================================================
' --------------------------------------------------
' 按行粘贴
' --------------------------------------------------
' 从指定 sheet 的指定位置开始，将当前数组引用中的数据以整行的形式拷贝到各个单元格中
' 写入方向：从指定位置开始，向右写入数据
'
' @param sh sheet 对象
' @param startRowNo 拷贝的起始行号
' @param startCol 拷贝的起始列号（数值型） 或者 列名（字符串型）
Public Sub IPrimitiveArrayRef_pasteToSheetAsRow( _
    ByRef sh As Worksheet, ByRef startRowNo As Long, ByRef startCol As Variant, _
    Optional ByRef startIndex As Long = 0, Optional ByRef endIndex As Variant, _
    Optional ByRef step As Long = 1)

    ' 1. 检查参数
    ' 1.1 如果 `sh == Nothing`，则抛出异常
    If sh Is Nothing Then
        Err.Raise 59803, Description:="PrimitiveArrayRefSlice.IPrimitiveArrayRef_pasteToSheetAsRow: Worksheet param[sh] is Nothing"
    End If

    ' 1.2 如果 startRowNo 超过了安全范围，则抛出异常
    If startRowNo <= 0 Then
        Err.Raise 59809, Description:="PrimitiveArrayRefSlice.IPrimitiveArrayRef_pasteToSheetAsRow: param[startRowNo] must not be less than to 0 And greater than UtilsWorksheet.MAX_ROW_NO. Now startRowNo=" & startRowNo
    ElseIf startRowNo > UtilsWorksheet.MAX_ROW_NO Then
        Err.Raise 59809, Description:="PrimitiveArrayRefSlice.IPrimitiveArrayRef_pasteToSheetAsRow: param[startRowNo] must not be less than to 0 And greater than UtilsWorksheet.MAX_ROW_NO. Now startRowNo=" & startRowNo
    End If

    ' 1.3 将 startCol 转换成真实的列号
    Dim realStartColNo As Integer
    realStartColNo = UtilsWorksheet.convertVariantColToColNo(startCol)

    ' 2. 写出数据
    doPasteToSheetAsRow sh, startRowNo, realStartColNo, _
                        startIndex, endIndex, step
End Sub

' 从指定 sheet 的指定位置开始，将当前数组引用中的数据以整行的形式拷贝到各个单元格中
' 写入方向：从指定位置开始，向右写入数据
'
' @param sh sheet 对象
' @param startRowNo 拷贝的起始行号
' @param startColNo 拷贝的起始列号（数值型） 或者 列名（字符串型）
Public Sub IPrimitiveArrayRef_pasteToSheetAsRowByColNo( _
    ByRef sh As Worksheet, ByRef startRowNo As Long, ByRef startColNo As Integer, _
    Optional ByRef startIndex As Long = 0, Optional ByRef endIndex As Variant, _
    Optional ByRef step As Long = 1)

    ' 1. 检查参数
    ' 1.1 如果 `sh == Nothing`，则抛出异常
    If sh Is Nothing Then
        Err.Raise 59803, Description:="PrimitiveArrayRefSlice.IPrimitiveArrayRef_pasteToSheetAsRowByColNo: Worksheet param[sh] is Nothing"
    End If

    ' 1.2 如果 startRowNo 超过了安全范围，则抛出异常
    If startRowNo <= 0 Then
        Err.Raise 59809, Description:="PrimitiveArrayRefSlice.IPrimitiveArrayRef_pasteToSheetAsRowByColNo: param[startRowNo] must not be less than to 0 And greater than UtilsWorksheet.MAX_ROW_NO. Now startRowNo=" & startRowNo
    ElseIf startRowNo > UtilsWorksheet.MAX_ROW_NO Then
        Err.Raise 59809, Description:="PrimitiveArrayRefSlice.IPrimitiveArrayRef_pasteToSheetAsRowByColNo: param[startRowNo] must not be less than to 0 And greater than UtilsWorksheet.MAX_ROW_NO. Now startRowNo=" & startRowNo
    End If

    ' 1.3  如果 startColNo 超过了安全范围，则抛出异常
    If startColNo <= 0 Then
        Err.Raise 59805, Description:="PrimitiveArrayRefSlice.IPrimitiveArrayRef_pasteToSheetAsRowByColNo: param[startColNo] must not be less than or equal to 0 And greater than UtilsWorksheet.MAX_COL_NO. Now startColNo= " & startColNo
    ElseIf startColNo > UtilsWorksheet.MAX_COL_NO Then
        Err.Raise 59805, Description:="PrimitiveArrayRefSlice.IPrimitiveArrayRef_pasteToSheetAsRowByColNo: param[startColNo] must not be less than or equal to 0 And greater than UtilsWorksheet.MAX_COL_NO. Now startColNo= " & startColNo
    End If

    ' 2. 写出数据
    doPasteToSheetAsRow sh, startRowNo, startColNo, _
                        startIndex, endIndex, step
End Sub

' 从指定 sheet 的指定位置开始，将当前数组引用中的数据以整行的形式拷贝到各个单元格中
' 写入方向：从指定位置开始，向右写入数据
'
' @param sh sheet 对象
' @param startRowNo 拷贝的起始行号
' @param startColName 拷贝的起始列名
Public Sub IPrimitiveArrayRef_pasteToSheetAsRowByColName( _
    ByRef sh As Worksheet, ByRef startRowNo As Long, ByRef startColName As String, _
    Optional ByRef startIndex As Long = 0, Optional ByRef endIndex As Variant, _
    Optional ByRef step As Long = 1)

    ' 1. 检查参数
    ' 1.1 如果 `sh == Nothing`，则抛出异常
    If sh Is Nothing Then
        Err.Raise 59803, Description:="PrimitiveArrayRefSlice.IPrimitiveArrayRef_pasteToSheetAsRowByColName: Worksheet param[sh] is Nothing"
    End If

    ' 1.2 如果 startRowNo 超过了安全范围，则抛出异常，异常码 `59809`
    If startRowNo <= 0 Then
        Err.Raise 59809, Description:="PrimitiveArrayRefSlice.IPrimitiveArrayRef_pasteToSheetAsRowByColName: param[startRowNo] must not be less than to 0 And greater than UtilsWorksheet.MAX_ROW_NO. Now startRowNo=" & startRowNo
    ElseIf startRowNo > UtilsWorksheet.MAX_ROW_NO Then
        Err.Raise 59809, Description:="PrimitiveArrayRefSlice.IPrimitiveArrayRef_pasteToSheetAsRowByColName: param[startRowNo] must not be less than to 0 And greater than UtilsWorksheet.MAX_ROW_NO. Now startRowNo=" & startRowNo
    End If

    ' 1.3 将 startColName 转换成真实的列号
    Dim realStartColNo As Integer
    realStartColNo = UtilsWorksheet.colStr2No(startColName)
    If realStartColNo <= 0 Then
        Err.Raise 59806, Description:="PrimitiveArrayRefSlice.IPrimitiveArrayRef_pasteToSheetAsRowByColName: param[startColName] must not be less than to A And greater than UtilsWorksheet.MAX_COL_STR. Now startColName= " & startColName
    ElseIf startColNo > UtilsWorksheet.MAX_COL_NO Then
        Err.Raise 59806, Description:="PrimitiveArrayRefSlice.IPrimitiveArrayRef_pasteToSheetAsRowByColName: param[startColName] must not be less than to A And greater than UtilsWorksheet.MAX_COL_STR. Now startColName= " & startColName
    End If

    ' 2. 写出数据
    doPasteToSheetAsRow sh, startRowNo, realStartColNo, _
                        startIndex, endIndex, step
End Sub

' 从指定 sheet 的指定位置开始，将当前数组引用中的数据以整行的形式拷贝到各个单元格中
Private Sub doPasteToSheetAsRow( _
    ByRef sh As Worksheet, ByRef startRowNo As Long, ByRef startColNo As Integer, _
    ByRef startIndex As Long, ByRef endIndex As Variant, _
    ByRef step As Long)

    ' 1. 计算需要的数据的范围
    Dim arrRange As Variant
    arrRange = UtilsArray.computeArraySliceInfo(sliceMaxIndex + 1, startIdx, endIndex, step)

    Dim dataStart As Long
    Dim dataStep As Long
    Dim dataCount As Long
    dataStart = arrRange(0)
    dataStep = arrRange(2)
    dataCount = arrRange(3)

    ' 2. 生成写入时的最大列号
    ' 2.1 计算写入时的最大列号
    Dim endColNo As Integer
    endColNo = startColNo + sliceMaxIndex

    ' 2.2 如果最大列号已经超过了列号的最大有效值，则重置
    If endColNo > UtilsWorksheet.MAX_COL_NO Then
        endColNo = UtilsWorksheet.MAX_COL_NO

        dataCount = endColNo - startColNo + 1
    End If

    ' 3. 写出数据
    With sh
        Dim i As Long
        Dim sliceIndex As Long
        For i = 0 To dataCount - 1
            sliceIndex = dataStart + i * dataStep

            .Cells(startRowNo, startColNo + i) = delegatedArrRef.unsafeItem(sliceStartIndex + sliceIndex * sliceStep)
        Next
    End With
End Sub

' --------------------------------------------------
' 按列粘贴
' --------------------------------------------------
' 从指定 sheet 的指定位置开始，将当前数组引用中的数据以整列的形式拷贝到各个单元格中
' 写入方向：从指定位置开始，向下写入数据
'
' @param sh sheet 对象
' @param startRowNo 拷贝的起始行号
' @param startCol 拷贝的起始列号（数值型） 或者 列名（字符串型）
Public Sub IPrimitiveArrayRef_pasteToSheetAsCol( _
    ByRef sh As Worksheet, ByRef startRowNo As Long, ByRef startCol As Variant, _
    Optional ByRef startIndex As Long = 0, Optional ByRef endIndex As Variant, _
    Optional ByRef step As Long = 1)

    ' 1. 检查参数
    ' 1.1 如果 `sh == Nothing`，则抛出异常
    If sh Is Nothing Then
        Err.Raise 59803, Description:="PrimitiveArrayRefSlice.IPrimitiveArrayRef_pasteToSheetAsCol: Worksheet param[sh] is Nothing"
    End If

    ' 1.2 如果 startRowNo 超过了安全范围，则抛出异常，异常码 `59809`
    If startRowNo <= 0 Then
        Err.Raise 59809, Description:="PrimitiveArrayRefSlice.IPrimitiveArrayRef_pasteToSheetAsCol: param[startRowNo] must not be less than to 0 And greater than UtilsWorksheet.MAX_ROW_NO. Now startRowNo=" & startRowNo
    ElseIf startRowNo > UtilsWorksheet.MAX_ROW_NO Then
        Err.Raise 59809, Description:="PrimitiveArrayRefSlice.IPrimitiveArrayRef_pasteToSheetAsCol: param[startRowNo] must not be less than to 0 And greater than UtilsWorksheet.MAX_ROW_NO. Now startRowNo=" & startRowNo
    End If

    ' 1.3 将 startCol 转换成真实的列号
    Dim realStartColNo As Integer
    realStartColNo = UtilsWorksheet.convertVariantColToColNo(startCol)

    ' 2. 写入数据
    doPasteToSheetAsRow sh, startRowNo, realStartColNo, _
                        startIndex, endIndex, step
End Sub

' 从指定 sheet 的指定位置开始，将当前数组引用中的数据以整列的形式拷贝到各个单元格中
' 写入方向：从指定位置开始，向下写入数据
'
' @param sh sheet 对象
' @param startRowNo 拷贝的起始行号
' @param startColNo 拷贝的起始列号
Public Sub IPrimitiveArrayRef_pasteToSheetAsColByColNo( _
    ByRef sh As Worksheet, ByRef startRowNo As Long, ByRef startColNo As Integer, _
    Optional ByRef startIndex As Long = 0, Optional ByRef endIndex As Variant, _
    Optional ByRef step As Long = 1)

    ' 1. 检查参数
    ' 1.1 如果 `sh == Nothing`，则抛出异常
    If sh Is Nothing Then
        Err.Raise 59803, Description:="PrimitiveArrayRefSlice.IPrimitiveArrayRef_pasteToSheetAsColByColNo: Worksheet param[sh] is Nothing"
    End If

    ' 1.2 如果 startRowNo 超过了安全范围，则抛出异常，异常码 `59809`
    If startRowNo <= 0 Then
        Err.Raise 59809, Description:="PrimitiveArrayRefSlice.IPrimitiveArrayRef_pasteToSheetAsColByColNo: param[startRowNo] must not be less than to 0 And greater than UtilsWorksheet.MAX_ROW_NO. Now startRowNo=" & startRowNo
    ElseIf startRowNo > UtilsWorksheet.MAX_ROW_NO Then
        Err.Raise 59809, Description:="PrimitiveArrayRefSlice.IPrimitiveArrayRef_pasteToSheetAsColByColNo: param[startRowNo] must not be less than to 0 And greater than UtilsWorksheet.MAX_ROW_NO. Now startRowNo=" & startRowNo
    End If

    ' 1.3  如果 startColNo 超过了安全范围，则抛出异常
    If startColNo <= 0 Then
        Err.Raise 59805, Description:="PrimitiveArrayRefSlice.IPrimitiveArrayRef_pasteToSheetAsColByColNo: param[startColNo] must not be less than or equal to 0 And greater than UtilsWorksheet.MAX_COL_NO. Now startColNo= " & startColNo
    ElseIf startColNo > UtilsWorksheet.MAX_COL_NO Then
        Err.Raise 59805, Description:="PrimitiveArrayRefSlice.IPrimitiveArrayRef_pasteToSheetAsColByColNo: param[startColNo] must not be less than or equal to 0 And greater than UtilsWorksheet.MAX_COL_NO. Now startColNo= " & startColNo
    End If

    ' 2. 写入数据
    doPasteToSheetAsCol sh, startRowNo, startColNo, _
                        startIndex, endIndex, step
End Sub

' 从指定 sheet 的指定位置开始，将当前数组引用中的数据以整列的形式拷贝到各个单元格中
' 写入方向：从指定位置开始，向下写入数据
'
' @param sh sheet 对象
' @param startRowNo 拷贝的起始行号
' @param startColName 拷贝的起始列名
Public Sub IPrimitiveArrayRef_pasteToSheetAsColByColName( _
    ByRef sh As Worksheet, ByRef startRowNo As Long, ByRef startColName As String, _
    Optional ByRef startIndex As Long = 0, Optional ByRef endIndex As Variant, _
    Optional ByRef step As Long = 1)

    ' 1. 检查参数
    ' 1.1 如果 `sh == Nothing`，则抛出异常
    If sh Is Nothing Then
        Err.Raise 59803, Description:="PrimitiveArrayRefSlice.IPrimitiveArrayRef_pasteToSheetAsColByColName: Worksheet param[sh] is Nothing"
    End If

    ' 1.2 如果 startRowNo 超过了安全范围，则抛出异常，异常码 `59809`
    If startRowNo <= 0 Then
        Err.Raise 59809, Description:="PrimitiveArrayRefSlice.IPrimitiveArrayRef_pasteToSheetAsColByColName: param[startRowNo] must not be less than to 0 And greater than UtilsWorksheet.MAX_ROW_NO. Now startRowNo=" & startRowNo
    ElseIf startRowNo > UtilsWorksheet.MAX_ROW_NO Then
        Err.Raise 59809, Description:="PrimitiveArrayRefSlice.IPrimitiveArrayRef_pasteToSheetAsColByColName: param[startRowNo] must not be less than to 0 And greater than UtilsWorksheet.MAX_ROW_NO. Now startRowNo=" & startRowNo
    End If

    ' 1.3 将 startColName 转换成真实的列号
    Dim realStartColNo As Integer
    realStartColNo = UtilsWorksheet.colStr2No(startColName)
    If realStartColNo <= 0 Then
        Err.Raise 59806, Description:="PrimitiveArrayRefSlice.IPrimitiveArrayRef_pasteToSheetAsColByColName: param[startColName] must not be less than to A And greater than UtilsWorksheet.MAX_COL_STR. Now startColName= " & startColName
    ElseIf startColNo > UtilsWorksheet.MAX_COL_NO Then
        Err.Raise 59806, Description:="PrimitiveArrayRefSlice.IPrimitiveArrayRef_pasteToSheetAsColByColName: param[startColName] must not be less than to A And greater than UtilsWorksheet.MAX_COL_STR. Now startColName= " & startColName
    End If

    ' 2. 写入数据
    doPasteToSheetAsCol sh, startRowNo, realStartColNo, _
                        startIndex, endIndex, step
End Sub

' 从指定 sheet 的指定位置开始，将当前数组引用中的数据以整列的形式拷贝到各个单元格中
Private Sub doPasteToSheetAsCol( _
    ByRef sh As Worksheet, ByRef startRowNo As Long, ByRef startColNo As Integer, _
    ByRef startIndex As Long, ByRef endIndex As Variant, _
    ByRef step As Long)

    ' 1. 计算需要的数据的范围
    Dim arrRange As Variant
    arrRange = UtilsArray.computeArraySliceInfo(sliceMaxIndex + 1, startIdx, endIndex, step)

    Dim dataStart As Long
    Dim dataStep As Long
    Dim dataCount As Long
    dataStart = arrRange(0)
    dataStep = arrRange(2)
    dataCount = arrRange(3)

    ' 2. 生成写入时的最大列号
    ' 2.1 计算写入时的最大列号
    Dim endRowNo As Integer
    endRowNo = startRowNo + sliceMaxIndex

    ' 2.2 如果最大列号已经超过了列号的最大有效值，则重置
    If endRowNo > UtilsWorksheet.MAX_COL_STR Then
        endRowNo = UtilsWorksheet.MAX_COL_STR

        dataCount = endRowNo - startRowNo + 1
    End If

    ' 3. 写出数据
    With sh
        Dim i As Long
        Dim sliceIndex As Long
        For i = 0 To dataCount - 1
            sliceIndex = dataStart + i * dataStep

            .Cells(startRowNo + i, startColNo) = delegatedArrRef.unsafeItem(sliceStartIndex + sliceIndex * sliceStep)
        Next
    End With
End Sub